Skip to content

Spring AOP 与声明式事务

一、AOP 配置

Spring AOP 底层是动态代理(JDK 动态代理 / CGLIB),proxy-target-class=true 强制走 CGLIB。三种声明方式:

方式做法
纯 XML<aop:config> + <aop:aspect> + <aop:before pointcut="execution(...)"/>
XML + 注解开启 <aop:aspectj-autoproxy proxy-target-class="true"/>,切面类用 @Aspect/@Before
纯注解配置类加 @EnableAspectJAutoProxy,切面用 @Aspect + 通知注解

二、声明式事务配置

本质是 AOP 把事务增强织入到业务方法上(事务管理器 = DataSourceTransactionManager)。

方式做法
纯 XML<tx:advice>propagation/isolation/read-only<aop:advisor advice-ref pointcut="execution(...)"/>
XML + 注解<tx:annotation-driven transaction-manager="..."/> + 在类/方法上加 @Transactional
纯注解配置类加 @EnableTransactionManagement,方法上 @Transactional

@Transactional 关键属性:propagation(传播)、isolation(隔离)、readOnlytimeoutrollbackFor

三、事务传播行为(7 种,重点 3 种)

定义视角:站在被调用方法 B 的角度看"当前已有事务时 B 怎么办"。日常开发前两个(REQUIRED / SUPPORTS)最常用。

传播行为行为
REQUIRED(默认)当前有事务就加入,没有就新建。B 抛异常会连 A 一起回滚
SUPPORTS当前有事务则加入,没有就以非事务方式执行
MANDATORY必须在已有事务中运行;无事务则抛 IllegalTransactionStateException
REQUIRES_NEW挂起当前事务,新建一个完全独立的事务
NOT_SUPPORTED以非事务方式执行,并挂起任何现有事务
NEVER必须非事务;若已有事务则抛异常
NESTED在当前事务内开启一个嵌套子事务(基于 JDBC Savepoint)

REQUIRED 的两种易错组合

  • 外部 A 无事务、内部 B 有 REQUIRED:B 异常只回滚 B,不影响 A。
  • 外部 A 有 REQUIRED、内部 B 无事务:B 异常会把 A 一起回滚(B 实际并入 A 的事务)。

REQUIRES_NEW vs NESTED(面试高频)

  • REQUIRES_NEW:内外层是两个互不相干的事务。内层提交后,外层后续失败不能回滚内层;需要 JTA 事务管理器支持。
  • NESTED:内层是外层的真正子事务,靠 Savepoint 实现。内层失败只回滚到 savepoint(外层其余操作继续);外层失败会连内层一起回滚。仅 DataSourceTransactionManager + JDBC 3.0+ 驱动支持,且需 nestedTransactionAllowed=true
  • 典型用法:NESTED 适合"主流程 + 日志记录"——日志那步失败不影响主流程,但主流程失败整体回滚。

常见误解

  • "REQUIRES_NEW 内层失败会导致外层回滚" —— 错,两者完全独立。
  • "NESTED 和 REQUIRES_NEW 差不多" —— 错,NESTED 外层回滚会带走内层,REQUIRES_NEW 不会。
  • "声明式事务加了 @Transactional 就一定生效" —— 还需 proxy-target-class/代理生效、方法经 Spring 代理调用(自调用失效)等前提。

关联

  • 上游:SpringAOP事务传播行为详解 · SpringAOP配置 · Spring声明式事务配置
  • 同域:[Spring IOC容器初始化与循环依赖](./Spring IOC容器初始化与循环依赖)(事务/代理增强都经过 BeanPostProcessor 织入,与三级缓存的 getEarlyBeanReference 同源)
  • 域地图:Java后端
最近更新